import * as Cesium from "cesium";
import * as turf from "@turf/turf";

/**
 * 代码来自贾宛龙开源：https://jiawanlong.github.io/Cesium-Examples/examples/cesiumEx/editor.html#4.1.7%E3%80%81%E6%96%B9%E9%87%8F%E5%88%86%E6%9E%90
 * @class DEUGlobe.Analysis.measureVolume
 * @category  分析
 * @classdesc 方量分析直接调用new DEUGlobe.Analysis.measureVolume()即可，左键画线，右键停止
 * @param {Object} viewer -  Cesium.viewer。
 * @param {Object} options - 参数。
 * @param {Boolean} options.terrainLevel -  terrainLevel。 查询当前地形级别(默认 13)
 */
export default class MeasureVolume {
  constructor(viewer, options) {
    var _this = this;
    this.viewer = viewer;
    this.optS = options || {};
    this.handler = null;
    this.polygon = null;
    this.wall = null;
    this.initHeight = 0;
    this.minHeight = 0;
    this.maxHeight = 0;
    this.volumn = 0;
    this.label = null;
    this.cartesian = null;
    this.poly = null;
    this.area = null;
    this.cellsize = null;
    this.centroid = null;
    this.planeHeight = 0;
    this.positions = [];
    this.Cartesian2Array = [];
    this.floatingPointLis = [];
    viewer.scene.globe.depthTestAgainstTerrain = true;
    this.data = [];
    this.PolygonPrimitive = (function () {
      function _(positions) {
        this.options = {
          name: "多边形",
          polygon: {
            show: true,
            perPositionHeight: true,
            material: Cesium.Color.CHARTREUSE.withAlpha(0.5),
          },
        };
        this.hierarchy = positions;
        this._init();
      }
      _.prototype._init = function () {
        var _self = this;
        var _update = function () {
          return new Cesium.PolygonHierarchy(_self.hierarchy);
        };
        //实时更新polygon.positions
        this.options.polygon.hierarchy = new Cesium.CallbackProperty(
          _update,
          false
        );
        _this.polygon = _this.viewer.entities.add(this.options);
      };
      return _;
    })();

    var tooltip = document.createElement("div");
    tooltip.setAttribute("class", "roamWidget_tooltip");
    var tooltipArrow = document.createElement("div");
    tooltipArrow.setAttribute("class", "roamWidget_tooltip_arrow");
    tooltip.appendChild(tooltipArrow);
    var tooltipInner = document.createElement("div");
    tooltipInner.setAttribute("class", "roamWidget_tooltip_inner");
    tooltip.appendChild(tooltipInner);
    tooltipInner.innerHTML = "单击 开始绘制";
    this.tooltip = tooltip;
    this.tooltipInner = tooltipInner;
  }
  /**
   * @classdesc 方量分析
   * @param {function} callback -  绘制完成回调
   */
  initMeasureVolume(callback) {
    var _this = this;
    _this.positions = [];
    _this.Cartesian2Array = [];
    _this.handler = new Cesium.ScreenSpaceEventHandler(
      _this.viewer.scene.canvas
    );
    _this.tooltip.style.left = "-100000px";
    _this.tooltip.style.top = "-100000px";
    document.getElementsByTagName("body")[0].appendChild(_this.tooltip);
    _this.viewer.scene.canvas.style.cursor = "crosshair";
    _this.handler.setInputAction(function (movement) {
      var ray = _this.viewer.camera.getPickRay(movement.endPosition);
      _this.cartesian = _this.viewer.scene.globe.pick(ray, _this.viewer.scene);
      if (_this.cartesian == undefined) {
        return;
      }
      if (_this.positions.length >= 2) {
        if (!Cesium.defined(_this.poly)) {
          _this.poly = new _this.PolygonPrimitive(_this.positions);
        } else {
          if (_this.cartesian != null) {
            _this.positions.pop();
            _this.cartesian.y += 1 + Math.random();
            _this.positions.push(_this.cartesian);
          }
        }
      }

      _this.tooltip.style.left = movement.endPosition.x + 20 + "px";
      _this.tooltip.style.top = movement.endPosition.y - 10 + "px";
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    _this.handler.setInputAction(function (movement) {
      var ray = _this.viewer.camera.getPickRay(movement.position);
      _this.cartesian = _this.viewer.scene.globe.pick(ray, _this.viewer.scene);
      if (_this.cartesian == undefined) {
        return;
      }
      if (_this.positions.length == 0) {
        _this.positions.push(_this.cartesian.clone());
      }
      _this.positions.push(_this.cartesian);
      _this.tooltipInner.innerHTML = "单击增加点，右键结束";
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    _this.handler.setInputAction(function (movement) {
      _this.viewer.entities.remove(_this.polygon);
      document.getElementsByTagName("body")[0].removeChild(_this.tooltip);
      _this.viewer.scene.canvas.style.cursor = "";
      _this.handler.destroy(); //关闭事件句柄
      _this.positions.pop(); //最后一个点无效
      var ellipsoid = _this.viewer.scene.globe.ellipsoid;
      _this.positions.forEach((element) => {
        var cartographic = ellipsoid.cartesianToCartographic(element);
        var lat = Cesium.Math.toDegrees(cartographic.latitude);
        var lng = Cesium.Math.toDegrees(cartographic.longitude);
        _this.Cartesian2Array.push([lng, lat]);
      });
      _this.Cartesian2Array.push(_this.Cartesian2Array[0]);

      let polygon = turf.polygon([_this.Cartesian2Array]);
      _this.cellsize = Math.sqrt(turf.area(polygon) / 10000);

      let enveloped = turf.envelope(polygon);
      _this.area = turf.area(polygon);
      _this.centroid = turf.centroid(polygon);

      let bbox = turf.bbox(enveloped);
      let grid = turf.pointGrid(bbox, _this.cellsize, { units: "meters" });
      let ptsWithin = turf.pointsWithinPolygon(grid, polygon);
      let lonlats = [];
      let features = ptsWithin.features;
      for (let i = 0; i < features.length; i++) {
        lonlats.push({
          lon: features[i].geometry.coordinates[0],
          lat: features[i].geometry.coordinates[1],
        });
      }
      TerrainToolCopy.LonlatPointsTerrainData(
        _this.viewer.terrainProvider,
        lonlats,
        _this.optS.terrainLevel,
        (positions) => {
          _this.getVolumn(positions);
          callback({
            planeHeight: _this.minHeight,
            wallMinHeight: _this.minHeight,
            wallMaxHeight: _this.maxHeight,
          });
        },
        _this.viewer
      );
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }
  initMeasure(positions) {
    var _this = this;
    _this.positions = [];
    _this.Cartesian2Array = [];
    _this.tooltip.style.left = "-100000px";
    _this.tooltip.style.top = "-100000px";
    var ellipsoid = _this.viewer.scene.globe.ellipsoid;

    _this.positions = positions;
    _this.positions.forEach((element) => {
      var cartographic = ellipsoid.cartesianToCartographic(element);
      var lat = Cesium.Math.toDegrees(cartographic.latitude);
      var lng = Cesium.Math.toDegrees(cartographic.longitude);
      _this.Cartesian2Array.push([lng, lat]);
    });
    _this.Cartesian2Array.push(_this.Cartesian2Array[0]);
    _this.minHeight = 3000;
    _this.initHeight = 4000;
    _this.maxHeight = 5000;
    let polygon = turf.polygon([_this.Cartesian2Array]);
    _this.cellsize = Math.sqrt(turf.area(polygon) / 10000);

    let enveloped = turf.envelope(polygon);
    _this.area = turf.area(polygon);
    _this.centroid = turf.centroid(polygon);

    let bbox = turf.bbox(enveloped);
    let grid = turf.pointGrid(bbox, _this.cellsize, { units: "meters" });
    let ptsWithin = turf.pointsWithinPolygon(grid, polygon);
    let lonlats = [];
    let features = ptsWithin.features;
    for (let i = 0; i < features.length; i++) {
      lonlats.push({
        lon: features[i].geometry.coordinates[0],
        lat: features[i].geometry.coordinates[1],
      });
    }
    TerrainToolCopy.LonlatPointsTerrainData(
      _this.viewer.terrainProvider,
      lonlats,
      _this.optS.terrainLevel,
      (positions) => {
        _this.getVolumn(positions, true);
        // callback({
        //   planeHeight: _this.minHeight,
        //   wallMinHeight: _this.minHeight,
        //   wallMaxHeight: _this.maxHeight,
        // });
      },
      _this.viewer
    );
  }
  getVolumn(data, flag) {
    var _this = this;
    _this.data = data;
    let positions = [];
    var ellipsoid = _this.viewer.scene.globe.ellipsoid;
    _this.positions.forEach((element) => {
      var cartographic = ellipsoid.cartesianToCartographic(element);
      let haiba = _this.viewer.scene.globe.getHeight(cartographic);
      positions.push(haiba);
      _this.floatingPoint = _this.viewer.entities.add({
        position: element,
        label: {
          text: "海拔:" + haiba.toFixed(2) + "米",
          font: "19px sans-serif",
          fillColor: Cesium.Color.GOLD,
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          outlineWidth: 2,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
          pixelOffset: new Cesium.Cartesian2(0.0, -20),
        },
      });
      _this.floatingPointLis.push(_this.floatingPoint);
    });
    if (!flag) {
      _this.minHeight = findMinHeight(positions);
      _this.initHeight = _this.planeHeight = _this.minHeight;
      _this.maxHeight = findMaxHeight(positions);
    }
    _this.volumn = 0;
    let a1 = 0,
      a2 = 0;
    for (let i = 0; i < data.length; i++) {
      _this.volumn +=
        (data[i]["height"] - _this.minHeight) * _this.cellsize * _this.cellsize;
    }
    let Cartesian3Array = [];
    let minimumHeights = [];
    let maximumHeights = [];
    _this.Cartesian2Array.forEach((element) => {
      console.log(element);
      element.forEach((element1, index) => {
        Cartesian3Array.push(element1);
        if (index == 1) {
          Cartesian3Array.push(0);
        }
      });
      minimumHeights.push(_this.minHeight);
      maximumHeights.push(_this.maxHeight);
    });
    _this.wall = _this.viewer.entities.add({
      wall: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(Cartesian3Array),
        minimumHeights: minimumHeights,
        maximumHeights: maximumHeights,
        outline: true,
        outlineColor: Cesium.Color.WHITE.withAlpha(0.7),
        material: Cesium.Color.CYAN.withAlpha(0.7),
      },
    });
    _this.polygon = _this.viewer.entities.add({
      polygon: {
        show: true,
        hierarchy: new Cesium.PolygonHierarchy(
          Cesium.Cartesian3.fromDegreesArrayHeights(Cartesian3Array)
        ),
        height: _this.minHeight,
        material: Cesium.Color.CHARTREUSE.withAlpha(0.5),
      },
    });
    let volumn = 0;
    let area = 0;
    if (_this.volumn / 10000 > 0.01) {
      volumn = (_this.volumn / 10000).toFixed(2) + "万立方米";
    } else {
      volumn = _this.volumn.toFixed(2) + "立方米";
    }
    if (_this.area / 10000 > 0.01) {
      area = (_this.area / 10000).toFixed(2) + "万平方米";
    } else {
      area = _this.area.toFixed(2) + "平方米";
    }
    _this.label = _this.viewer.entities.add({
      name: "飞机",
      position: Cesium.Cartesian3.fromDegrees(
        _this.centroid.geometry.coordinates[0],
        _this.centroid.geometry.coordinates[1],
        _this.maxHeight
      ),
      label: {
        text: "挖方体积：" + volumn + "\n" + "横切面积：" + area,
        font: "18px sans-serif",
        showBackground: true,
        fillColor: Cesium.Color.WHITE,
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        pixelOffset: new Cesium.Cartesian2(0.0, -80),
      },
    });
  }
  /**
   * @classdesc 设置参数
   * @param {Object} options - 参数。
   * @param {function} callback -  绘制完成回调
   */
  setParameters(opt, callback) {
    this.planeHeight =
      opt.planeHeight == undefined ? this.planeHeight : opt.planeHeight;
    this.minHeight =
      opt.wallMinHeight == undefined ? this.minHeight : opt.wallMinHeight;
    this.maxHeight =
      opt.wallMaxHeight == undefined ? this.maxHeight : opt.wallMaxHeight;
    if (this.planeHeight < this.minHeight) {
      this.minHeight = this.planeHeight;
    }

    if (this.planeHeight > this.maxHeight) {
      this.maxHeight = this.planeHeight;
    }
    if (Cesium.defined(this.polygon)) {
      this.polygon.polygon.height.setValue(this.planeHeight);
    }
    if (Cesium.defined(this.wall)) {
      let minimumHeights = [];
      for (
        let index = 0;
        index < this.wall.wall.minimumHeights.getValue().length;
        index++
      ) {
        minimumHeights.push(this.minHeight);
      }
      this.wall.wall.minimumHeights.setValue(minimumHeights);
      let maximumHeights = [];
      for (
        let index = 0;
        index < this.wall.wall.maximumHeights.getValue().length;
        index++
      ) {
        maximumHeights.push(this.maxHeight);
      }
      this.wall.wall.maximumHeights.setValue(maximumHeights);
    }
    this.calculateBulk();
    callback({
      planeHeight: this.planeHeight,
      wallMinHeight: this.minHeight,
      wallMaxHeight: this.maxHeight,
    });
  }
  //计算体积
  calculateBulk() {
    //基准面下的体积
    let planeBulk = this.area * (this.planeHeight - this.minHeight);
    //挖方体积
    let volumn = 0;
    this.volumn = 0;
    for (let i = 0; i < this.data.length; i++) {
      this.volumn +=
        (this.data[i]["height"] - this.minHeight) *
        this.cellsize *
        this.cellsize;
      if (this.data[i]["height"] > this.planeHeight) {
        volumn +=
          (this.data[i]["height"] - this.planeHeight) *
          this.cellsize *
          this.cellsize;
      }
    }
    let fillInBulk = 0;
    if (this.initHeight <= this.planeHeight) {
      //填方体积
      fillInBulk = planeBulk - (this.volumn - volumn);
    }

    if (fillInBulk > 0) {
      if (fillInBulk / 10000 > 0.01) {
        fillInBulk = (fillInBulk / 10000).toFixed(2) + "万立方米";
      } else {
        fillInBulk = fillInBulk.toFixed(2) + "立方米";
      }
      fillInBulk = "填方体积：" + fillInBulk + "\n";
    } else {
      fillInBulk = "";
    }
    if (volumn > 0) {
      if (volumn / 10000 > 0.01) {
        volumn = (volumn / 10000).toFixed(2) + "万立方米";
      } else {
        volumn = volumn.toFixed(2) + "立方米";
      }
      volumn = "挖方体积：" + volumn + "\n";
    } else {
      volumn = "";
    }
    let area = this.area;
    if (area / 10000 > 0.01) {
      area = (area / 10000).toFixed(2) + "万平方米";
    } else {
      area = area.toFixed(2) + "平方米";
    }
    this.label.label.text.setValue(fillInBulk + volumn + "横切面积：" + area);
  }
  //清除
  clear() {
    if (this.floatingPointLis.length > 0) {
      this.floatingPointLis.forEach((element) => {
        this.viewer.entities.remove(element);
      });
    }
    if (Cesium.defined(this.wall)) {
      this.viewer.entities.remove(this.wall);
    }
    if (Cesium.defined(this.polygon)) {
      this.viewer.entities.remove(this.polygon);
    }
    if (Cesium.defined(this.label)) {
      this.viewer.entities.remove(this.label);
    }
  }
}

function findMinHeight(e) {
  e = e.sort((a, b) => a - b);
  // let minHeight = 0;
  // let minPoint = null;
  // for (let i = 0; i < e.length; i++) {
  //     let height = e[i];
  //     if (height < minHeight) {
  //         minHeight = height;
  //     }
  // }
  return e[0];
}
function findMaxHeight(e) {
  e = e.sort((a, b) => b - a);
  return e[0];
}
//获取高程
var TerrainToolCopy = (function () {
  function _() {}

  //传入lonlat数组 角度制的lon lat
  _.LonlatPointsTerrainData = function (
    terrainProvider,
    lonlats,
    terrainLevel = 13,
    callback,
    viewer
  ) {
    var pointArrInput = [];
    for (var i = 0; i < lonlats.length; i++) {
      pointArrInput.push(
        Cesium.Cartographic.fromDegrees(lonlats[i].lon, lonlats[i].lat)
      );
    }
    // var promise = Cesium.sampleTerrain(terrainProvider, terrainLevel, pointArrInput);//pointArrInput
    // Cesium.when(promise, function (updatedPositions) {
    //     callback(updatedPositions);
    // });
    Cesium.sampleTerrainMostDetailed(
      viewer.terrainProvider,
      pointArrInput
    ).then((samples) => {
      callback(samples);
    });
  };

  //传入Cartographic类型数组 弧度制经纬度
  _.CartographicPointsTerrainData = function (
    terrainProvider,
    Cartographics,
    callback,
    viewer
  ) {
    if (Cartographics.length && Cartographics.length > 0) {
    } else {
      return;
    }
    // var promise = Cesium.sampleTerrain(terrainProvider, terrainLevel, Cartographics);//pointArrInput
    // Cesium.when(promise, function (updatedPositions) {
    //     callback(updatedPositions);
    // });
    Cesium.sampleTerrainMostDetailed(
      viewer.terrainProvider,
      Cartographics
    ).then((samples) => {
      callback(samples);
    });
  };
  return _;
})();
